package utimer

/*******************************************************************************
Copyright:cloud
Author:cloudapex@126.com
Version:1.0
Date:2020-06-16
Description: 定时器对象
注: 由于是个独立模块, 自己的回调函数如果异常会导致崩溃. (捕获恢复不好做,日志没地方输出)
*******************************************************************************/
import (
	"reflect"
	"time"
)

var the *MTimer

func init() { the = (&MTimer{}).Init(); go loop() }

type timer struct {
	begin    time.Time
	daily    bool
	interval time.Duration
}

type MTimer struct {
	timers map[reflect.Value]*timer
}

func (this *MTimer) Init() *MTimer {
	this.timers = make(map[reflect.Value]*timer)
	return this
}
func (this *MTimer) TimerHandler(d time.Duration, tickFun TimerHandFunc, right ...bool) {
	this.adTimHandler(d, tickFun, false, right...)
}
func (this *MTimer) DayerHandler(after0 time.Duration, tickFun TimerHandFunc, right ...bool) {
	this.adTimHandler(after0, tickFun, true, right...)
}
func (this *MTimer) UnTimHandler(tickFun TimerHandFunc) {
	delete(this.timers, reflect.ValueOf(tickFun))
}
func (this *MTimer) adTimHandler(d time.Duration, tickFun TimerHandFunc, dayStly bool, right ...bool) {
	if len(right) > 0 && right[0] && !tickFun(time.Now()) {
		return
	}
	this.timers[reflect.ValueOf(tickFun)] = &timer{time.Now(), dayStly, d}
}
func (this *MTimer) TickTimer() {
	if len(this.timers) == 0 {
		return
	}

	curTime := time.Now()
	for fun, val := range this.timers {
		if val.daily {
			if curTime.YearDay() == val.begin.YearDay() {
				continue
			}
			time0 := time.Date(curTime.Year(), curTime.Month(), curTime.Day(), 0, 0, 0, 0, curTime.Location())
			if curTime.Sub(time0) <= val.interval {
				continue
			}
		} else {
			if curTime.Sub(val.begin) <= val.interval {
				continue
			}
		}

		val.begin = curTime
		if ret := fun.Call([]reflect.Value{reflect.ValueOf(curTime)}); !ret[0].Bool() {
			delete(this.timers, fun)
			continue
		}
	}
}

//------------------------------------------------------------------------------
func loop() {
	t := time.NewTicker(time.Millisecond * time.Duration(1000.0/float32(1)))
	for {
		select {
		case <-t.C:
			the.TickTimer()
		}
	}
}
